package com.framework.core.mybatis.ddl.mysql;

import com.framework.core.mybatis.context.FieldInfo;
import com.framework.core.mybatis.context.TableContext;
import com.framework.core.mybatis.context.TableInfo;
import com.framework.core.mybatis.ddl.AbstractMybatisDdlFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 主要执行类-mysql支持
 * created by zzg
 * 2024-12-06
 */
public class MysqlDdlFactory extends AbstractMybatisDdlFactory {
    @Override
    public void execute(TableContext context) {
        super.generalStatements(context);
    }

    @Override
    protected List<String> createTableStatements(List<TableInfo> tableInfoList) {
        List<String> statements = new ArrayList<>();
        for (TableInfo it : tableInfoList) {
            StringBuilder createStatement = new StringBuilder("CREATE TABLE ");
            createStatement.append(it.getTableName()).append(" (\n");
            List<FieldInfo> fieldInfoList = it.getFieldInfoList();
            StringBuilder primaryKey = new StringBuilder();
            for (int i = 0; i < fieldInfoList.size(); i++) {
                FieldInfo fieldInfo = fieldInfoList.get(i);
                addColumn(fieldInfo, createStatement);

                if (i < fieldInfoList.size() - 1) {
                    createStatement.append(",\n");
                }

                if (fieldInfo.isPrimaryKey()) {
                    primaryKey.append("PRIMARY KEY (");
                    primaryKey.append(fieldInfo.getValue());
                    primaryKey.append(") USING BTREE");
                }
            }
            if (!ObjectUtils.isEmpty(primaryKey)) {
                createStatement.append(",\n");
                createStatement.append(primaryKey.toString());
            }
            createStatement.append(");");
            statements.add(createStatement.toString());
        }
        return statements;
    }

    @Override
    protected List<String> updateTableStatements(List<TableInfo> existsList) {
        List<String> statements = new ArrayList<>();
        for (TableInfo it : existsList) {
            // 数据库字段
            List<String> selectColumns = selectColumns(it.getTableName());
            // 实体类字段
            List<FieldInfo> fieldInfoList = it.getFieldInfoList();
            boolean hasChange = false;
            if (selectColumns.size() != fieldInfoList.size()) {
                // 判断是否缺失
                hasChange = true;
            }
            if (!hasChange) {
                continue;
            }
            // 过滤掉已有的
            fieldInfoList = fieldInfoList.stream().filter(fieldInfo -> !selectColumns.contains(fieldInfo.getValue()))
                    .collect(Collectors.toList());
            // 没有新增
            if (ObjectUtils.isEmpty(fieldInfoList)) {
                continue;
            }
            // 创建语句
            StringBuilder alterStatement = new StringBuilder("ALTER TABLE ");
            alterStatement.append(it.getTableName()).append(" ADD(\n");
            for (int i = 0; i < fieldInfoList.size(); i++) {
                FieldInfo fieldInfo = fieldInfoList.get(i);
                addColumn(fieldInfo, alterStatement);

                if (i < fieldInfoList.size() - 1) {
                    alterStatement.append(",\n");
                }
            }
            alterStatement.append(");");
            statements.add(alterStatement.toString());
        }
        return statements;
    }

    private void addColumn(FieldInfo fieldInfo, StringBuilder statement) {
        String property = fieldInfo.getProperty();
        if (ObjectUtils.isEmpty(property)) {
            property = getPropertyByFieldType(fieldInfo);
        }
        statement
                .append(fieldInfo.getValue())
                .append(" ")
                .append(property);

        if (StringUtils.isNotBlank(fieldInfo.getComment())) {
            statement
                    .append(" COMMENT '")
                    .append(fieldInfo.getComment())
                    .append("'");
        }
    }

    private String getPropertyByFieldType(FieldInfo fieldInfo) {
        boolean primaryKey = fieldInfo.isPrimaryKey();
        if (primaryKey) {
            return " varchar(32) NOT NULL";
        }
        String result = " varchar(255) DEFAULT ''";
        JdbcType jdbcType = fieldInfo.getJdbcType();
        switch (jdbcType) {
            case TIMESTAMP:
                result = " datetime DEFAULT NULL";
                break;
            case DATE:
                result = " date DEFAULT NULL";
                break;
            case TIME:
                result = " time DEFAULT NULL";
                break;
            case NUMERIC:
            case DECIMAL:
                result = " decimal(20,2) DEFAULT 0";
                break;
            case INTEGER:
                result = " int DEFAULT 0";
                break;
            case TINYINT:
                result = " tinyint DEFAULT 0";
                break;
            case SMALLINT:
                result = " smallint DEFAULT 0";
                break;
            case BIGINT:
                result = " bigint DEFAULT 0";
                break;
            case FLOAT:
                result = " float DEFAULT 0";
                break;
            case DOUBLE:
                result = " double DEFAULT 0";
                break;
            case REAL:
                result = " double DEFAULT 0";
                break;
            case BIT:
                result = " bit DEFAULT 0";
                break;
            case BOOLEAN:
                result = " boolean DEFAULT 0";
                break;
            case CHAR:
                result = " char(1) DEFAULT ''";
                break;
            case VARCHAR:
                result = " varchar(255) DEFAULT ''";
                break;
            case LONGVARCHAR:
                result = " text";
                break;
            case BINARY:
                result = " binary(255)";
                break;
            case VARBINARY:
                result = " varbinary(255)";
                break;
            case LONGVARBINARY:
                result = " blob";
                break;
            case BLOB:
                result = " blob";
                break;
            case CLOB:
                result = " text";
                break;
            case NULL:
                result = " null";
                break;
            case OTHER:
                result = " other";
                break;
            case JAVA_OBJECT:
                result = " blob";
                break;
            case DISTINCT:
                result = " distinct";
                break;
            case STRUCT:
                result = " blob";
                break;
            case ARRAY:
                result = " blob";
                break;
            case REF:
                result = " ref";
                break;
            case DATALINK:
                result = " datalink";
                break;
        }

        return result;
    }
}
